<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="author" content="凉凉" />
  <title>iGEM 2023 寒假培训内容</title>
  <style>
    html {
      line-height: 1.5;
      font-family: Georgia, serif;
      font-size: 20px;
      color: #1a1a1a;
      background-color: #fdfdfd;
    }
    body {
      margin: 0 auto;
      max-width: 36em;
      padding-left: 50px;
      padding-right: 50px;
      padding-top: 50px;
      padding-bottom: 50px;
      hyphens: auto;
      overflow-wrap: break-word;
      text-rendering: optimizeLegibility;
      font-kerning: normal;
    }
    @media (max-width: 600px) {
      body {
        font-size: 0.9em;
        padding: 1em;
      }
      h1 {
        font-size: 1.8em;
      }
    }
    @media print {
      body {
        background-color: transparent;
        color: black;
        font-size: 12pt;
      }
      p, h2, h3 {
        orphans: 3;
        widows: 3;
      }
      h2, h3, h4 {
        page-break-after: avoid;
      }
    }
    p {
      margin: 1em 0;
    }
    a {
      color: #1a1a1a;
    }
    a:visited {
      color: #1a1a1a;
    }
    img {
      max-width: 100%;
    }
    h1, h2, h3, h4, h5, h6 {
      margin-top: 1.4em;
    }
    h5, h6 {
      font-size: 1em;
      font-style: italic;
    }
    h6 {
      font-weight: normal;
    }
    ol, ul {
      padding-left: 1.7em;
      margin-top: 1em;
    }
    li > ol, li > ul {
      margin-top: 0;
    }
    blockquote {
      margin: 1em 0 1em 1.7em;
      padding-left: 1em;
      border-left: 2px solid #e6e6e6;
      color: #606060;
    }
    code {
      font-family: Menlo, Monaco, 'Lucida Console', Consolas, monospace;
      font-size: 85%;
      margin: 0;
    }
    pre {
      margin: 1em 0;
      overflow: auto;
    }
    pre code {
      padding: 0;
      overflow: visible;
      overflow-wrap: normal;
    }
    .sourceCode {
     background-color: transparent;
     overflow: visible;
    }
    hr {
      background-color: #1a1a1a;
      border: none;
      height: 1px;
      margin: 1em 0;
    }
    table {
      margin: 1em 0;
      border-collapse: collapse;
      width: 100%;
      overflow-x: auto;
      display: block;
      font-variant-numeric: lining-nums tabular-nums;
    }
    table caption {
      margin-bottom: 0.75em;
    }
    tbody {
      margin-top: 0.5em;
      border-top: 1px solid #1a1a1a;
      border-bottom: 1px solid #1a1a1a;
    }
    th {
      border-top: 1px solid #1a1a1a;
      padding: 0.25em 0.5em 0.25em 0.5em;
    }
    td {
      padding: 0.125em 0.5em 0.25em 0.5em;
    }
    header {
      margin-bottom: 4em;
      text-align: center;
    }
    #TOC li {
      list-style: none;
    }
    #TOC ul {
      padding-left: 1.3em;
    }
    #TOC > ul {
      padding-left: 0;
    }
    #TOC a:not(:hover) {
      text-decoration: none;
    }
    code{white-space: pre-wrap;}
    span.smallcaps{font-variant: small-caps;}
    span.underline{text-decoration: underline;}
    div.column{display: inline-block; vertical-align: top; width: 50%;}
    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
    ul.task-list{list-style: none;}
    pre > code.sourceCode { white-space: pre; position: relative; }
    pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
    pre > code.sourceCode > span:empty { height: 1.2em; }
    .sourceCode { overflow: visible; }
    code.sourceCode > span { color: inherit; text-decoration: inherit; }
    div.sourceCode { margin: 1em 0; }
    pre.sourceCode { margin: 0; }
    @media screen {
    div.sourceCode { overflow: auto; }
    }
    @media print {
    pre > code.sourceCode { white-space: pre-wrap; }
    pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
    }
    pre.numberSource code
      { counter-reset: source-line 0; }
    pre.numberSource code > span
      { position: relative; left: -4em; counter-increment: source-line; }
    pre.numberSource code > span > a:first-child::before
      { content: counter(source-line);
        position: relative; left: -1em; text-align: right; vertical-align: baseline;
        border: none; display: inline-block;
        -webkit-touch-callout: none; -webkit-user-select: none;
        -khtml-user-select: none; -moz-user-select: none;
        -ms-user-select: none; user-select: none;
        padding: 0 4px; width: 4em;
        color: #aaaaaa;
      }
    pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
    div.sourceCode
      {   }
    @media screen {
    pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
    }
    code span.al { color: #ff0000; font-weight: bold; } /* Alert */
    code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
    code span.at { color: #7d9029; } /* Attribute */
    code span.bn { color: #40a070; } /* BaseN */
    code span.bu { } /* BuiltIn */
    code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
    code span.ch { color: #4070a0; } /* Char */
    code span.cn { color: #880000; } /* Constant */
    code span.co { color: #60a0b0; font-style: italic; } /* Comment */
    code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
    code span.do { color: #ba2121; font-style: italic; } /* Documentation */
    code span.dt { color: #902000; } /* DataType */
    code span.dv { color: #40a070; } /* DecVal */
    code span.er { color: #ff0000; font-weight: bold; } /* Error */
    code span.ex { } /* Extension */
    code span.fl { color: #40a070; } /* Float */
    code span.fu { color: #06287e; } /* Function */
    code span.im { } /* Import */
    code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
    code span.kw { color: #007020; font-weight: bold; } /* Keyword */
    code span.op { color: #666666; } /* Operator */
    code span.ot { color: #007020; } /* Other */
    code span.pp { color: #bc7a00; } /* Preprocessor */
    code span.sc { color: #4070a0; } /* SpecialChar */
    code span.ss { color: #bb6688; } /* SpecialString */
    code span.st { color: #4070a0; } /* String */
    code span.va { color: #19177c; } /* Variable */
    code span.vs { color: #4070a0; } /* VerbatimString */
    code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
    .display.math{display: block; text-align: center; margin: 0.5rem auto;}
  </style>
  
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
</head>
<body>
<header id="title-block-header">
<h1 class="title">iGEM 2023 寒假培训内容</h1>
<p class="author">凉凉</p>
</header>
<h1 id="课程安排">课程安排</h1>
<h2 id="年前">年前</h2>
<p>主要是复习和复现:</p>
<ul>
<li>复习上个学期介绍的主要内容:
<ul>
<li>程序语言和形式语法 (视时间安排, 可以略去)</li>
<li>程序编写和阅读 (以 C 和 Python 为主)
<ul>
<li>查文档的方法 (man, 等等)</li>
<li>写一些简单的控制用的逻辑代码 (作业和课堂演示)</li>
</ul></li>
<li>单片机控制方面 (以 NodeMCU 为主, 或者找模拟器)
<ul>
<li><a href="https://www.withdiode.com">模拟器 (Diode)</a> 或者 <a
href="https://wokwi.com">模拟器 (Wokwi)</a> 都是在线的呢.
<ul>
<li>简单的电路和引脚介绍</li>
<li>其他 (?)</li>
</ul></li>
<li>或者自购 NodeMCU (?)</li>
</ul></li>
<li>建模 (以 SW 为主)</li>
<li>焊接 (略)</li>
</ul></li>
<li>复现去年的 iGEM 项目
<ul>
<li>程序部分
<ul>
<li>(课堂) 读代码并解释代码中的部分去年没讲的和单片机相关的知识点,
可以改写部分的代码.</li>
<li>(作业) 改写代码 (挖空? 或者别的形式? ) 或者写拓展</li>
</ul></li>
<li>建模部分
<ul>
<li>在去年的项目中选择一些零件来复现 (作业和课堂演示)</li>
</ul></li>
</ul></li>
<li>文献调研 (作业)
<ul>
<li>去阅读 iGEM Best Hardware 并记录</li>
<li>看看能不能每节课抽一些时间来读 Best Hardware 或者在上课的时候用 Best
Hardware 里面的 Project 来做例子.</li>
</ul></li>
</ul>
<h2 id="年后">年后</h2>
<p>看项目是否确定了要做什么:</p>
<ul>
<li>若确定了明年的项目:
<ul>
<li>以项目的方向为主去做一些教学</li>
</ul></li>
<li>若还没确定:
<ul>
<li>分组去实现一些硬件的项目</li>
</ul></li>
</ul>
<h1 id="年后-1">[年后]</h1>
<h2 id="项目一-pid-控制器">项目一: PID 控制器</h2>
<p>可以参考的文章: <a href="https://ruby-china.org/topics/40791">用 Ruby
实现飞机自动驾驶仪</a>.</p>
<p>我们会从最简单的控制部分讲起, 并且不会涉及非常多的专业的知识,
保证以实用和简单为主. <del>毕竟难的也不会</del>.</p>
<p>注: 这个教学的例子为了方便快速准备, 我用的是我比较熟练的 Ruby.
不过基本上不会特别利用 Ruby 中的高级操作, 所以和伪代码差不多.
如果课上有要求换语言的话, 我到时候可以重新写.</p>
<p>我们会用到的例子如下: (我会尽量去找一些和硬件组有关的例子,
不过受限于我目前没法搞到实物, 所以大部分的例子都是代码模拟运行的.
所有的示例代码都在 <code
class="verbatim">classes/winter-class-04/</code> 中.)</p>
<p>现在我们想要控制一个容器内的温度保持在一个范围附近, 比如说保持在
<code class="verbatim">37.5</code> 附近.</p>
<details><summary>一个温度控制的模型</summary>

<p>该例子中的模型 <code class="verbatim">Bottle</code> 在 <code
class="verbatim">classes/winter-class-04/bottle.rb</code> 中声明,
我们在使用的时候只需要 <code class="verbatim">load 'bottle.rb'</code>
即可调用. (或 <code class="verbatim">require_relative</code>)</p>
<ul>
<li>其中通过 <code class="verbatim">bottle = Bottle.new</code>
声明一个新的容器 <code class="verbatim">bottle</code>.</li>
<li>通过 <code class="verbatim">bottle.on</code> 或者 <code
class="verbatim">bottle.off</code> 来打开或关闭加热器</li>
<li>通过 <code class="verbatim">bottle.temp</code> 来读取当前的 <code
class="verbatim">bottle</code> 的温度</li>
<li>通过 <code class="verbatim">bottle.update</code> 来模拟下一帧</li>
</ul>
<p>一个例子如下: 容器从初始温度 <code class="verbatim">16.0</code>
升温到室温 <code class="verbatim">20.0</code></p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode ruby"><code class="sourceCode ruby"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">load</span> <span class="vs">&#39;bottle.rb&#39;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>bottle <span class="kw">=</span> <span class="dt">Bottle</span><span class="at">.new</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="fu">puts</span> <span class="st">&quot;t\ttemp&quot;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="dv">1000</span><span class="at">.times</span> <span class="kw">{</span> bottle<span class="at">.update</span> <span class="kw">}</span></span></code></pre></div>
<pre class="example"><code>t temp
0 16.0
1.0   18.54
2.0   19.46
3.0   19.8
4.0   19.93
5.0   19.97
6.0   19.99
7.0   20.0
8.0   20.0
9.0   20.0
</code></pre>
</details>

<h3 id="控制的类型">控制的类型</h3>
<p>那么在上面的温度控制程序中, 一个很自然的控制想法就是:
<strong>如果温度低了就加热, 如果温度到了就停止加热</strong>.
于是我们的控制代码就变成类似这样的东西:</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode ruby"><code class="sourceCode ruby"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">load</span> <span class="vs">&#39;bottle.rb&#39;</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>bottle <span class="kw">=</span> <span class="dt">Bottle</span><span class="at">.new</span>(<span class="wa">refresh: </span><span class="kw">-</span><span class="dv">1</span>, <span class="wa">heat_capacity: </span>c, <span class="wa">watt: </span>w)</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>data <span class="kw">=</span> <span class="kw">[]</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="dv">1000</span><span class="at">.times</span> <span class="cf">do</span> <span class="kw">|</span>i<span class="kw">|</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  bottle<span class="at">.update</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>  <span class="co"># This is the main body of control system</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>  bottle<span class="at">.on</span> <span class="cf">if</span> bottle<span class="at">.temp</span> <span class="kw">&lt;</span> <span class="fl">37.5</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>  bottle<span class="at">.off</span> <span class="cf">if</span> bottle<span class="at">.temp</span> <span class="kw">&gt;</span> <span class="fl">37.5</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>  data <span class="kw">&lt;&lt;</span> <span class="kw">[</span>bottle<span class="at">.t.round</span>(<span class="dv">2</span>), bottle<span class="at">.temp.round</span>(<span class="dv">2</span>), bottle<span class="at">.heater</span><span class="kw">]</span> \</span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> i <span class="kw">%</span> <span class="dv">10</span> <span class="kw">==</span> <span class="dv">0</span> <span class="co"># &amp;&amp; i &lt; 1000</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a><span class="cf">end</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>data</span></code></pre></div>
<p>对于这种简单的控制思路, 我们称之为 Open-Loop 开环控制.
(请不必纠结名称, 在介绍了之后的 Closed-Loop 闭环控制后,
这个名字就非常显然了. )</p>
<p>实际上这个控制包含了两个部分:</p>
<ul>
<li>启动 heater 控制部分</li>
<li>关闭 heater 控制部分</li>
</ul>
<p><img src="./pic/ruby-temp-control-example.svg" /></p>
<p>但是可以从上面的温度变化图中看到,
上面的控制系统会导致要控制的温度会在一个范围进行波动.</p>
<p>尽管我们可以通过增加容器的热容量 <code
class="verbatim">heat_capacity</code>, 或者减少加热器运转的功率 <code
class="verbatim">watt</code> 来减少最终稳定的温度变化.
但是这样会导致受外界变化较大且预热时间较长.</p>
<h1 id="年前-冬季学期没教完的东西">[年前] 冬季学期没教完的东西</h1>
<h2 id="solidworks">SolidWorks</h2>
<p>通过复现上一次的工程机器来教学.</p>
<ul>
<li>每个人挑选一个 (或者几个) 去年的工程图中的零件, 在 SW
中重新实现</li>
<li>其他要求?</li>
<li>实现一些机械零件结构, 可以照网上的教程来做都行</li>
</ul>
<h2 id="c-及编程">C 及编程</h2>
<p>通过复现去年的控制部分代码.</p>
<ul>
<li><p>阅读去年的代码, 了解:</p>
<ul>
<li><code class="verbatim">Serial</code> 和波特率</li>
<li>以及其他不懂的…</li>
</ul>
<p>然后画出或者写出对代码核心的控制流程图 (<code
class="verbatim">loop</code> 函数, 比如)</p></li>
<li><p>重写 <code class="verbatim">loop</code> 函数中对温度控制的代码.
(可以在控制理论讲完之后重写)</p></li>
<li><p>往里面加一些自己喜欢的东西</p></li>
</ul>
<h2 id="第一节课">第一节课</h2>
<ul>
<li>复习 (请课后或者课前) 阅读 <strong>形式化的 C 的介绍</strong>
部分</li>
<li><strong>基本</strong> 从零开始, 用项目来介绍 C 的编程</li>
</ul>
<h3 id="上学期-形式化的-c-的介绍">(上学期) 形式化的 C 的介绍</h3>
<details>
<summary>形式化的 C 的介绍</summary>

<p>该部分推荐的参考书为 C Programming Language by K&amp;R, 以及 C: A
Referrence Manual. 不过我对 C 语言也不是很熟练,
所以如果文档中有任何问题, 请在 issue 中提出.</p>
<p>在该部分的 C 语言中, 我们主要使用的是 C 和汇编的对应来进行简单的教学.
不过请注意, 这样的教学仅仅只是最基础的简单教学.</p>
<p>(注: 其中汇编的代码为 <code>gcc -S -fverbose code.c</code> 编译得到,
编译的环境为 Ubuntu 21.10 (GNU/Linux 5.13.0-48-generic
x86<sub>64</sub>), 如果你不想要复杂的输出的话, 可以选择去掉
<code>-fverbose</code> flag, 通过 <code>gcc -S code.c</code> 来编译.
)</p>
<ol>
<li><p>变量, 表达式, 值, 数组, 结构</p>
<ol>
<li><p>变量 <span id="c-assignment"></span></p>
<p>我们可以用
<code>&lt;value-type-option&gt; &lt;variable-name&gt; (= &lt;variable-value&gt;){0,1};</code>
的方式来为一个变量赋予名字. 这就像是在计算机中找了一个空间来储存变量名字
所对应的变量的值.</p>
<p>其中 <code>&lt;value-type-option&gt;</code> 为变量的值的类型,
我们将会在下面简单介绍, 目前你可以简单认为有如下的形式:</p>
<pre class="text"><code>&lt;assignment&gt; ::= &lt;type-opt&gt; (&lt;equation&gt;)+;
&lt;type-opt&gt; ::= &quot;int&quot;    /* 整型 */
       | &quot;float&quot;  /* 浮点数 */
       | &quot;double&quot; /* 双精度 */
       | &quot;char&quot;   /* 字符值 */
       /* 不完整的规则 */
&lt;equation&gt; ::= &lt;variable-name&gt; (= &lt;variable-value&gt;){0,1}
</code></pre>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">// assignment</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> x <span class="op">=</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> y<span class="op">;</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>y <span class="op">=</span> <span class="dv">2</span><span class="op">;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>printf<span class="op">(</span><span class="st">&quot;%d %d&quot;</span><span class="op">,</span> x<span class="op">,</span> y<span class="op">);</span></span></code></pre></div>
<p>(思考题: 请在阅读后文的 <a href="#c-value">3.3.1.1.3</a>
一部分后写出更加规范的形式定义. )</p>
<p>比如:</p>
<div class="sourceCode" id="cb6" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> x <span class="op">=</span> <span class="dv">3</span><span class="op">;</span></span></code></pre></div>
<p>在汇编中, 我们可以看到这样的操作对应了下面的代码:</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode asm"><code class="sourceCode fasm"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a># main<span class="op">.</span>c<span class="op">:</span><span class="dv">3</span><span class="op">:</span>   int x <span class="op">=</span> <span class="dv">3</span><span class="co">;</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>  movl    <span class="op">$</span><span class="bn">3</span><span class="op">,</span> <span class="op">-</span><span class="dv">4</span><span class="op">(%</span><span class="kw">rbp</span><span class="op">)</span>    <span class="op">#,</span> x</span></code></pre></div>
<p>我们会发现, 变量 <code>x</code> 被放在了相对 <code>rbp</code> 位置为
<code>-4</code> 的栈上. (关于变量和栈,
我们在计算机的运行中介绍了大概的基本知识. 如果你觉得这部分比较困难,
请认为 <code>rbp</code> 就是一个记录局部变量在哪里的标志,
有点像是局部坐标的坐标原点, 而 <code>-4</code> 则是一个变量的坐标.
)</p></li>
<li><p>表达式 <span id="c-expression"></span></p>
<p>我们可以通过像数学一样来写简单的表达式:</p>
<p>比如:</p>
<div class="sourceCode" id="cb8" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> x <span class="op">=</span> <span class="dv">3</span><span class="op">;</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> y<span class="op">;</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>y <span class="op">=</span> <span class="op">(</span>x <span class="op">*</span> x <span class="op">+</span> <span class="dv">1</span><span class="op">)</span> <span class="op">/</span> <span class="dv">10</span><span class="op">;</span> <span class="co">// y = 1</span></span></code></pre></div>
<p>一个简单的表达式的形式如下:</p>
<pre class="text"><code>&lt;exp&gt; ::= &lt;binary-exp&gt; | &lt;single-exp&gt;
&lt;binary-exp&gt; ::= (&lt;exp&gt; &lt;binary-operator&gt; &lt;exp&gt;) | &lt;val&gt;
&lt;single-exp&gt; ::= (&lt;prefix-op&gt; &lt;val&gt;) | (&lt;val&gt; &lt;subfix-op&gt;)
&lt;binary-operator&gt; ::= &quot;+&quot; | &quot;-&quot; | &quot;*&quot; | &quot;/&quot; /* 四则运算 */
          | &quot;&amp;&quot; | &quot;^&quot; | &quot;|&quot; | &quot;&lt;&lt;&quot; | &quot;&gt;&gt;&quot; /* 位运算 */
          | &quot;&amp;&amp;&quot; | &quot;||&quot; | &quot;&lt;=&quot; | &quot;&gt;=&quot; | &quot;==&quot; | &quot;!=&quot; /* 逻辑运算 */
          /* 其他, 留作课后作业 */
&lt;prefix-op&gt; ::= &quot;++&quot; | &quot;--&quot; /* 运算后取值 */
        | &quot;&amp;&quot; | &quot;*&quot; /* 取地址和取指向的值 */
        /* 其他, 留作课后作业 */
&lt;val&gt; ::= /* 变量名, 字面值, 函数返回值 */
</code></pre>
<p>我们自然可以像数学一样使用函数来作为返回值: <code>f(x)</code>.
具体的内容见后面的函数调用.</p></li>
<li><p>值 <span id="c-value"></span></p>
<p>在 C 语言中有不同的值的类型.
(其实为什么会有不同的值的类型的原因非常简单, 工程上来说,
是因为不同的值在计算机中的储存和表示方式不同; 或者也可以数学一些来说,
就是其表示的域的不同, 对于考虑的问题的不同, 选择不同的精度和值的类型.
)</p>
<p>(注: 这里是工程, 所以是会有误差的. 所以在编程语言中的值,
大多数是数值近似值, 而不是一个解析的准确值. )</p>
<blockquote>
<p>狄拉克: 工程学对我最大的启发就是工程学能够容忍误差.</p>
<p>from 赵亚溥 力学</p>
</blockquote>
<p>可以参考 <a
href="https://en.wikipedia.org/wiki/C_data_types">维基百科</a> 中对 C
Data Type 的说明, 我们可以发现, 在 C 语言中有四种主要的类型:</p>
<ul>
<li>char 字符, 在形式上为单引号括起来的一个字符, 在计算机储存的时候,
实际上仍然是用数的形式储存, 只是通过表格映射到字符的形状.</li>
<li>int 整型, 在形式上为整数</li>
<li>float 浮点数, 在形式上为小数, 或者写成整数的形式也不是不行</li>
<li>double 双精度, 就顾名思义.</li>
</ul>
<p>在这四种类型之上, 可以通过添加修饰符来定义值的更加细节的表现:</p>
<ul>
<li>unsigned, signed, 是否有符号位,
实际上可以和计算机中的值的表示方式一起学习</li>
<li>long, long long, 在原本的基础上进行拓展</li>
</ul>
<p>不过实际上, 基本目前我们可能就只会使用非常少的几种类型.
大家只需要了解如何写就好了. (意思就是说, 更加细节的部分可以自学,
或者可以在 issue 中提出等. )</p></li>
<li><p>数组</p>
<p>在 <a href="#c-assignment">3.3.1.1.1</a> 一部分中,
我们提到了定义的变量会在栈上通过一个相对 <code>bp</code> 的位移来定位,
假如我们想要定义一系列有序的变量, 比如 <code>a1</code>, <code>a2</code>,
<code>a3</code> 等等. 除了直接通过 <code>int a1, a2, a3, ...</code>
的方式来定义, (这样定义的变量, 应该是在栈上依次排布的,
于是一个非常简单的想法就是直接用相对位移来访问, 如: 令 <code>a1</code>
的地址为 <code>&amp;a1</code>, 于是加上一个相对位移
<code>&amp;a1 + delta</code>, 就可以得到 <code>&amp;a2</code> 的地址…
于是以次类推. )</p>
<p>这样利用相对位移的方式来定义一系列的有序变量的方式,
就是一种简单的数组的形式.</p>
<p>那么在形式上, 数组的定义像这样
<code>&lt;type-opt&gt; &lt;array-name&gt;[&lt;array-size&gt;];</code>,
或者
<code>&lt;type-opt&gt; &lt;array-name&gt;={&lt;array-values&gt;};</code>:</p>
<div class="sourceCode" id="cb10" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> a<span class="op">[</span><span class="dv">5</span><span class="op">];</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> b<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span><span class="dv">1</span><span class="op">,</span> <span class="dv">2</span><span class="op">,</span> <span class="dv">3</span><span class="op">,</span> <span class="dv">4</span><span class="op">,</span> <span class="dv">5</span><span class="op">};</span></span></code></pre></div>
<p>于是我们就能够通过 <code>&lt;array-name&gt;[&lt;index&gt;]</code>
的方式像变量一样去访问对应的元素.</p>
<p>其中, <code>&lt;type-opt&gt;</code> 定义了数组中的元素的值的类型, 而
<code>&lt;array-size&gt;</code> 定义了数组的大小.</p>
<p>在 C 语言中, 字符串也就是一种数组:</p>
<div class="sourceCode" id="cb11" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="dt">char</span> s1<span class="op">[]</span> <span class="op">=</span> <span class="st">&quot;I am Lucky&quot;</span><span class="op">;</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="dt">char</span> s2<span class="op">[]</span> <span class="op">=</span> <span class="op">{</span><span class="ch">&#39;L&#39;</span><span class="op">,</span> <span class="ch">&#39;u&#39;</span><span class="op">,</span> <span class="ch">&#39;c&#39;</span><span class="op">,</span> <span class="ch">&#39;k&#39;</span><span class="op">,</span> <span class="ch">&#39;y&#39;</span><span class="op">,</span> <span class="ch">&#39;\0&#39;</span><span class="op">};</span></span></code></pre></div>
<p>只是通过 <code>'\0'</code> 符号来标记字符串的结束.</p>
<p>那么说到数组, 就不得不提起指针.
目前我们仅仅从形式上来理解指针的定义和调用, 在 <a
href="#c-function">3.3.1.3.1</a> 一节,
我会介绍一些我们为什么要使用指针以及指针该如何去理解的知识.</p>
<p>我们用符号 <code>*</code> 来表示这个是一个指向某某的指针, 用
<code>&amp;</code> 来表示这个是某某的地址.</p>
<p>简单来说, 假如我们有这样的一个 Excel 表格:</p>
<table>
<tbody>
<tr class="odd">
<td>index</td>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>…</td>
</tr>
</tbody>
</table>
<table style="width:58%;">
<colgroup>
<col style="width: 11%" />
<col style="width: 5%" />
<col style="width: 5%" />
<col style="width: 5%" />
<col style="width: 5%" />
<col style="width: 5%" />
<col style="width: 5%" />
<col style="width: 5%" />
<col style="width: 8%" />
</colgroup>
<tbody>
<tr class="odd">
<td>value</td>
<td>%</td>
<td>)</td>
<td><ul>
<li></li>
</ul></td>
<td><ul>
<li></li>
</ul></td>
<td>&lt;</td>
<td>&gt;</td>
<td>?</td>
<td>…</td>
</tr>
</tbody>
</table>
<p>我们用序号去访问值, 比如在序号为 6 的地方是一个 <code>?</code>,
那么我们便可以用这样的方式说, 对于 <code>?</code>,
我们不妨说它就是我们名叫 <code>question_mark</code> 的变量的地址, 于是
<code>&amp;question_mark</code> 的值就应该是 <code>6</code>.
我们不妨将其用一个变量 <code>point_to_question_mark</code> 来表示, 即:
<code>point_to_question_mark = &amp;question_mark</code>.</p>
<p>同理, 对于上面的例子, 我们也能够说:
<code>*point_to_question_mark</code> 就是再说, 我们要去
<code>index</code> 为 <code>point_to_question_mark</code> 的值的地方,
也就是 <code>index</code> 为 6 的地方去找到一个值, 也就是
<code>?</code>.</p>
<p>这就是取地址和读地址的简单的理解. 那么具体有什么用呢?
<del>让你头晕脑花</del>.</p></li>
<li><p>结构</p>
<p>一个数组中的所有元素的类型都是一样的. 相当于在一个名字下,
按序组织了相同类型的元素.</p>
<p>那如果我们不太想要顺序性, 但是想要在一个名字底下,
存放不同类型的元素呢? (这里就是放弃顺序性而获得了更多的能力了. )</p>
<blockquote>
<p>那么, 古尔丹, 代价是什么呢?</p>
</blockquote>
<p>在 C 语言中, 这样的方式就是结构体:</p>
<div class="sourceCode" id="cb12" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> namingspace <span class="op">{</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">char</span> c<span class="op">;</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> x<span class="op">;</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> a<span class="op">[</span><span class="dv">5</span><span class="op">];</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> namingspace example<span class="op">;</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>example<span class="op">.</span>c <span class="op">=</span> <span class="ch">&#39;L&#39;</span><span class="op">;</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>example<span class="op">.</span>x <span class="op">=</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>example<span class="op">.</span>a<span class="op">[</span><span class="dv">0</span><span class="op">]</span> <span class="op">=</span> <span class="dv">2</span><span class="op">;</span></span></code></pre></div>
<p>在形式上, 通过
<code>struct &lt;name&gt; { &lt;c-assignment&gt;+ };</code>
的形式来定义一个 struct 类型. 使用
<code>struct &lt;struct-name&gt; &lt;v-name&gt;</code>
来定义一个结构体变量.</p></li>
<li><p>小练习</p>
<ol>
<li>尝试描述这些定义的形式语言</li>
<li>在看完后文中的程序结构后尝试编写一些程序来验证定义
<ul>
<li>定义一个变量 <code>pi</code>, 其值为 <code>3.14159265354</code>,
思考应该用什么类型</li>
<li>定义一个结构, 该结构可以用来定义一个:
<ul>
<li>二维平面上的一个点 <span
class="math inline">(<em>x</em>,<em>y</em>)</span> 或者 <span
class="math inline">(<em>ρ</em>,<em>θ</em>)</span> 以及其标签
<code>label</code></li>
<li>某一个时间点 <code>t</code> 采集的温度 <code>temp</code>, 流量
<code>q</code>, 成品率 <code>present</code> 等的数据</li>
</ul></li>
<li>(较难) 定义一个 <a
href="https://en.wikipedia.org/wiki/Linked_list">Linked List</a></li>
</ul></li>
<li>定义一个 <a
href="https://www.geeksforgeeks.org/multidimensional-arrays-c-cpp/">二维数组</a>,
用来储存一个平面格点上的不同点位中的菌体的温度. 思考该如何定位? (画饼)
思考, 如何通过红外摄像头来捕捉? 请设计一个简单的系统来描述.</li>
</ol></li>
</ol></li>
<li><p>控制流</p>
<p>其实在计算机的运行中我们已经接触过了控制流了.</p>
<p>其实基本的思想很简单,
就是如何让本来应该是线性的程序进行分支和循环.</p>
<ol>
<li><p>分支</p>
<p>在流程图中, 我们可以将分支画成下面这样:</p>
<p><img src="pic/programming-branching-fig.png" /></p>
<p>但是我们的代码肯定是一个线性的代码,
没有办法表现出像这样的丰富的网状结构. 但是一个非常简单的做法:
在程序中划出一些代码块, 如果条件成立, 就执行代码块, 否则就不执行代码块.
于是我们就有了 <code>if</code> 条件判断.</p>
<pre class="text"><code>&lt;if_branching&gt; ::= &quot;if (&quot; &lt;condition&gt; &quot;)&quot; &lt;code_block&gt;
&lt;condition&gt; ::= &lt;exp&gt;
&lt;code_block&gt; ::= &lt;line_exp&gt; | &quot;{&quot; &lt;line_exp&gt;* &quot;}&quot;
&lt;line_exp&gt; ::= &lt;exp&gt; &quot;;&quot;
&lt;exp&gt; ::= /* 应该不用写很多了吧? */
</code></pre>
<p>(注: 代码块的形式上的定义: <code>{ &lt;exp&gt;; }</code>)</p>
<p>举个例子:</p>
<div class="sourceCode" id="cb14" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> x <span class="op">=</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>x <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>  printf<span class="op">(</span><span class="st">&quot;x is greater than 0&quot;</span><span class="op">);</span></span></code></pre></div>
<p>当然, 除了单纯的 <code>if</code>, 还能够写更多的形式, 如双分支:
<code>if (condition) {} else {}</code>, 以及多条件分支:
<code>if (condition) {} else if {} else {}</code>. 以及多条件分支选择:
<code>switch (exp) { case val: break; }</code>.
(不过可以看作是一种语法糖. )</p></li>
<li><p>循环</p>
<p>在计算机的运行中, 我们介绍过了如何让程序循环. 同样的, 在 C 语言里面,
也有循环的结构:</p>
<div class="sourceCode" id="cb15" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> <span class="op">(</span>condition<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>你可以将其理解为:</p>
<div class="sourceCode" id="cb16"><pre
class="sourceCode asm"><code class="sourceCode fasm"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>.while_loop</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>  <span class="bu">cmp</span> condition</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">je</span> <span class="op">.</span>out</span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a><span class="co">;;;   do something</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>  <span class="cf">jmp</span> <span class="op">.</span>while_loop</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>.<span class="bu">out</span></span></code></pre></div>
<p>一般来说, 我们的循环都会有一个朴实而无华的想法, 就是历遍.
比如让传感器编号从 0 到 9, 检测每个传感器上的温度:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> sensor_index <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> <span class="op">(</span>sensor_index <span class="op">&lt;</span> <span class="dv">10</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>  read_sensor_tempture_at_index<span class="op">(</span>sensor_index<span class="op">);</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>  sensor_index<span class="op">++;</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>而这样的代码因为太过常见. <em>思考题</em>: 请给出代码的一个形式结构.
并解释这样的形式结构为何能够用后面的 <code>for</code> 来简化书写.
解答会在 <code>for</code> 的语法结构解释完后给出.</p>
<p>于是人们便创造出了一个语法叫 <code>for</code> 来简化书写.</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> sensor_index <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> sensor_index <span class="op">&lt;</span> <span class="dv">10</span><span class="op">;</span> sensor_index<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>  read_sensor_tempture_at_index<span class="op">(</span>sensor_index<span class="op">);</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span></code></pre></div>
<p>我们可以用一个图来描述上面的过程:</p>
<p><img src="pic/programming-loop-fig.png" /></p>
<p>前面思考题的解答</p>
<div class="sourceCode" id="cb19"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="op">&lt;</span>initial_code<span class="op">&gt;;</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> <span class="op">(&lt;</span>final_condition<span class="op">&gt;)</span> <span class="op">{</span></span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// do something</span></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">&lt;</span>step_code<span class="op">&gt;;</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> <span class="op">(&lt;</span>initial_code<span class="op">&gt;;</span> <span class="op">&lt;</span>final_condition<span class="op">&gt;;</span> <span class="op">&lt;</span>step_code<span class="op">&gt;)</span> <span class="op">{</span></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>  <span class="co">// do something</span></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div></li>
<li><p>小练习</p>
<ol>
<li>写一个条件判断来进行对输入信号的筛选. 比如我们有这样的一个热敏电阻:
<ul>
<li>在 <span class="math inline">10 ∼ 30<sup>∘</sup><em>C</em></span>
的时候, 电压和温度的关系近似有线性关系: <span
class="math inline"><em>u</em> = <em>u</em><sub>10</sub> + <em>λ</em><sub>1</sub>(<em>t</em>−<em>t</em><sub>10</sub>)</span>;</li>
<li>在 <span class="math inline">30 ∼ 50<sup>∘</sup><em>C</em></span>
的时候, 电压和温度的关系近似有二次关系: <span
class="math inline"><em>u</em> = <em>u</em><sub>20</sub> + <em>λ</em><sub>2</sub>(<em>t</em>−<em>t</em><sub>20</sub>) + <em>μ</em><sub>2</sub>(<em>t</em>−<em>t</em><sub>20</sub>)<sup>2</sup></span></li>
</ul></li>
<li>写一个简单的循环模型,
读取一个热敏电阻传感器阵列上每个温度传感器的数值, 然后打印出来.</li>
<li>请写一个 PID 控制, 使用简单的循环, 不必写得很规范.
<ul>
<li><p>关于 <a href="https://zh.wikipedia.org/wiki/PID控制器">PID</a>,
我们可以简单这样理解: 其包含比例 (Proportional), 积分 (Integral)
以及微分 (Derivative) 组成, 在数学上的公式如下:</p>
<p><span class="math display">$$u(t) = K_p e(t) + K_i \int_0^t e(\tau)
\mathrm{d}\tau + K_d \frac{\mathrm{d}}{\mathrm{d}t}e(t)$$</span></p>
<p>当然, 光看数学公式没什么鸟用, 实际上其中的参数十分好理解.</p></li>
<li><p><span
class="math inline"><em>K</em><sub><em>p</em></sub><em>e</em>(<em>t</em>)</span>
项:</p>
<p>我们用一个简单的例子来理解: 烧菜, 咸了加水, 淡了加盐. 用 <span
class="math inline"><em>e</em>(<em>t</em>)</span>
来量化表示我们的菜的咸度, 若 <span
class="math inline"><em>e</em>(<em>t</em>) &gt; 0</span>, 那么就是咸了,
反之就是淡了. 我们要做的机器人通过一个控制信号 <span
class="math inline"><em>u</em>(<em>t</em>)</span> 来决定是加盐
或者加水的量:</p>
<div class="sourceCode" id="cb20" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> p_control<span class="op">(</span><span class="dt">int</span> expect_flavor<span class="op">)</span> <span class="op">{</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> u<span class="op">;</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> e <span class="op">=</span> taste_soup <span class="op">-</span> expect_flavor<span class="op">;</span> <span class="op">;</span> e <span class="op">=</span> taste_soup <span class="op">-</span> expect_flavor<span class="op">)</span> <span class="op">{</span></span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">(</span>e <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a>      add_salt<span class="op">(</span>k_p <span class="op">*</span> e<span class="op">);</span></span>
<span id="cb20-6"><a href="#cb20-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb20-7"><a href="#cb20-7" aria-hidden="true" tabindex="-1"></a>      add_water<span class="op">(</span>k_p <span class="op">*</span> e<span class="op">);</span></span>
<span id="cb20-8"><a href="#cb20-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb20-9"><a href="#cb20-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb20-10"><a href="#cb20-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>但是一个简单的问题就是, 往往这样只会得到一个振荡的不衰减的信号.
因为它和我们的简谐振动方程冥冥中有那么一丝相像.</p>
<p><span
class="math display"><em>u</em>(<em>t</em>) = <em>K</em><sub><em>p</em></sub><em>e</em>(<em>t</em>) ⇔ <strong>F</strong> =  − <em>k</em><em>Δ</em><strong>x</strong></span></p></li>
<li><p><span class="math inline"><em>K</em><sub><em>d</em></sub></span>
项:</p>
<p>前文刚说了, PID 方程中的 P 项很像我们的简谐振动方程,
并且有一个信号变化幅度很难衰减, 也就是有会一直摆动的缺点.
那么一个自然的一个想法就是像阻尼振动的问题一样, 为其添加一个阻尼项.</p>
<p>于是, 我们就得到了 <span class="math inline">$K_d
\frac{\mathrm{d}}{\mathrm{d}t}e(t)$</span> 项.</p>
<p>或者, 我们可以用一个更加玄学的感觉来说, <span
class="math inline"><em>K</em><sub><em>d</em></sub></span>
项就像是一种扼制未来发展趋势的项: 假如信号有增加的趋势, <span
class="math inline"><em>e</em>′(<em>t</em>) &gt; 0</span>,
那么我们就要使用控制信号来将其减少.</p>
<p>举一个例子, 在温度控制中, 如果我们开始加热, 看到 <span
class="math inline"><em>e</em>(<em>t</em>) = <em>T</em><sub><em>e</em><em>x</em><em>p</em><em>e</em><em>c</em><em>t</em></sub> − <em>T</em><sub><em>n</em><em>o</em><em>w</em></sub></span>
逐渐减少, 但是 <span class="math inline"><em>e</em>′(<em>t</em>)</span>
却仍然很大, 就像是开车到了红灯的十字路口, 在衡量了一下相差的距离 <span
class="math inline"><em>e</em>(<em>t</em>)</span> 和当前的速度 <span
class="math inline"><em>e</em>′(<em>t</em>)</span>,
也许很自然的结论就是, 我们应该要降低一下车速, 也就是 <span
class="math inline"><em>K</em><sub><em>d</em></sub><em>e</em>′(<em>t</em>)</span>
的作用.</p></li>
<li><p><span class="math inline"><em>K</em><sub><em>i</em></sub></span>
项:</p>
<p>既然我们已经知道了 P 和 D 的作用, 那么 I 又是什么呢?</p>
<p>简单, 假设我们现在有一个电压输入转输出信号线性转换器
(我也不知道这个是什么, 我们可以先假装有那么一个东西),
但是由于我们用的是一个奇怪的神奇二极管,
导致我们输出的电压始终和我们预期的电压有一个 <span
class="math inline">0.3<em>V</em></span> 的压降, 这个时候,
我们希望能够调节输入 <span class="math inline"><em>u</em></span>
来使得输入和期望的差 <span class="math inline"><em>e</em></span>
能够减小到零.</p>
<p>但是很遗憾, 这个时候, 只靠 P 和 D 难以将这个偏差修改,
因为这就像是斜面上的阻尼振动, 我们的偏差值在平均中被 P 项忽略了, 而 D
项因为是变化项, 压根就不会理会常量偏差.</p>
<p>所以, 通过加入对信号的时间积分, 我们可以去修正这个常量的偏差.
直观上来看, 除非 <span class="math inline"><em>ē</em></span> 为零, 否则
I 项就会参与到控制信号中.</p></li>
<li><p>那么, 请思考该如何用一个循环来简单地实现类似的算法呢?
假设我们已经知道了其中所有的参数.</p></li>
<li><p>注: 对于上面的方程, 更好的处理方式是对其使用傅里叶变换, 或者
Laplace 变换, 然后就能够比较方便地计算.</p></li>
</ul></li>
</ol></li>
</ol></li>
<li><p>函数, IO, 系统调用</p>
<ol>
<li><p>函数 <span id="c-function"></span></p>
<p>和数学的函数不一样的地方在于, C 语言中的函数更像是一种子过程
(sub-progress).</p>
<p>(注: 如果有兴趣的话, 可以参考一下 Lisp 中的一些思想. )</p>
<p>我们先从形式上来看函数的定义以及函数的调用:</p>
<p>函数的定义:</p>
<pre class="text"><code>&lt;define_function&gt; ::= &lt;return_type&gt; &quot; &quot; &lt;function_name&gt; &quot;(&quot; &lt;args&gt; &quot;)&quot; &lt;function_block&gt;
&lt;return_type&gt; ::= &lt;value_type&gt;
&lt;function_name&gt; ::= &lt;vaild_name&gt;
&lt;function_block&gt; ::= &quot;{&quot; &quot;}&quot; /* code */
&lt;args&gt; ::= &lt;value_type&gt; &quot; &quot; &lt;vaild_name&gt;
&lt;vaild_name&gt; ::= ([a-z] | [A-Z]) ([a-z] | [A-Z] | [0-9] | &quot;_&quot; )*
&lt;value_type&gt; ::= &quot;char&quot; | &quot;int&quot; | &quot;float&quot; | &quot;double&quot;
</code></pre>
<p>函数的调用:</p>
<pre class="text"><code>&lt;function_call&gt; ::= &lt;function_name&gt; &quot;(&quot; &lt;function_args_value&gt; &quot;)&quot;
</code></pre>
<p>那么拿一个例子来看即可:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> puls_one<span class="op">(</span><span class="dt">int</span> x<span class="op">)</span> <span class="op">{</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> x <span class="op">+</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>这里我们就定义了一个加一函数.</p>
<p>函数的执行过程:</p>
<ul>
<li><p>我们可以将函数看作是一个子过程 (subprogress), 之所以这么叫,
是因为和所谓的 "主" 过程 (main progress)相比,
子过程十分类似于主过程</p></li>
<li><p>一个过程是 "局域" 的, 或者说, 这个过程是有极限的 (bushi).
这意味着这样的一个过程只能够掌握它周围一小部分区域的信息,
而如果想要了解更多, <del>得加钱</del>, 就会跳出这个过程.</p>
<p>于是我们就会面对许多的概念: 局部变量以及相对应的全局变量, 栈, 堆,
进程等等的概念.</p>
<ul>
<li><p><strong>栈</strong></p>
<p>在之前, 我们已经了解过栈是如何存放变量的:
我们通过用相对位置的方式来记录相对不同的变量.
就像是我们用相对原点的坐标来记录不同的点一样.</p>
<p>那么既然是相对位置, 我们还能够定义相对位置的相对位置:
这就像是物理里面的相对参考系. 于是应该不难这样构造: 我们认为,
子过程可以是相对主过程有一个位置, 然后对于过程来说,
自己所知道的变量都是相对自己为原点的.</p></li>
<li><p>于是我们就知道了局部变量, 也就是相对自己的变量,
这些变量是自己所能够掌握的. 这就像是每个家庭里面都有的 "dad", "mom"
变量, 但是这些变量对于不同的家庭并不一定具有相同的值.</p>
<p>(注: 这个例子, 在之后 Ruby 的 OOP (面向对象编程) 里面,
我们应该会再一次提到, 不过需要注意区分这个例子使用的目的的不同. )</p>
<p>而全局变量更像是一种经过约定之后, 大家都知道它在哪里的变量.</p></li>
</ul></li>
<li><p><strong>参数传递</strong></p>
<p>那么既然我们已经知道了, 一个过程是 "局域" 的这个事实,
那么一个简单的想法就是, 一个局域的过程是如何看到外界告诉它的信息呢?</p>
<p>这方法就是参数传递. 囿于篇幅有限, 以及能力有限,
这里我们只是给出一个可能的传递参数的方式: 通过栈来传递.
至于其他的传递参数的方式, 可以作为有兴趣的同学的拓展了解内容.</p>
<p>譬如我们一开始的局部坐标都是 <span
class="math inline"><em>O</em> + <em>i</em><em>n</em><em>d</em><em>e</em><em>x</em> = <em>v</em><em>a</em><em>r</em></span>,
但是为何不换一种思路, 让我们用负坐标来表示传入的参数, 如 <span
class="math inline"><em>O</em> − <em>i</em><em>n</em><em>d</em><em>e</em><em>x</em> = <em>a</em><em>r</em><em>g</em></span>,
于是我们就实现了简单的参数传递.</p>
<p>一个简单的例子:</p>
<table>
<tbody>
<tr class="odd">
<td>arg 2</td>
<td>arg 1</td>
<td>bp</td>
<td>var1</td>
<td>var2</td>
<td>sp</td>
</tr>
</tbody>
</table>
<p><del>——-</del>——-<del>—-</del>——<del>——</del>—-+</p>
<table>
<tbody>
<tr class="odd">
<td>-2</td>
<td>-1</td>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
</tbody>
</table>
<p>大概是这个样子. 当然, 实际绝对不是这样的就是了.</p>
<p>那么很明显, 你就会发现, 如果我们写了一个递归函数:</p>
<div class="sourceCode" id="cb24" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> sub_to_zero<span class="op">(</span><span class="dt">int</span> x<span class="op">)</span> <span class="op">{</span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(</span>x <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> sub_to_zero<span class="op">(</span>x <span class="op">-</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> x<span class="op">;</span></span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>那么当我们传入一个很大的参数 <code>x</code> 的时候,
我们的栈中就会堆积一堆的看起来很没有用的值: <code>x - 1</code>,
<code>x - 2</code>, … 这样是很愚蠢的.</p>
<ul>
<li><p>TOC 不过现代的编译器会使用一种叫做尾递归的方式,
将这种显然可以用循环来简化的函数用循环的方式来编译,
大大提高了程序的效率.</p>
<p>这种方法我们可能会在之后介绍.
目前我们可以暂时默认现代编译器已经为我们想了很多了.</p></li>
<li><p>指针, 就像是全局变量一样,
我们可以使用指针来告诉被递归调用的子函数,
你并不需要得到传入参数的完整的拷贝,
你只需要知道在哪里找到这个值就好了.</p>
<p>(就像是现代社会下, 不少老师会拿这个开刷学生:
你们不能说知道知识在哪里可以查到, 要背下来记在脑子里. 不过我持反对意见,
毕竟我连在哪里我都说不上来. )</p>
<p>这样的指针尤其是在传递数组, 结构的时候尤为方便,
因为于其将一整个数组完全拷贝到栈上作为参数传递,
肯定是直接传递指针来得更加方便.</p>
<p>并且传递指针, 而不是拷贝的值备份,
还能够满足我们想要修改栈上的数据的愿望.</p></li>
</ul></li>
<li><p>去哪里执行代码啊?</p>
<p>我们已经了解了这么多, 但是还有一个比较麻烦的小问题,
既然我们的程序是读取一个指针所指向的命令,
那么在跳转到一个函数的子过程的时候, 我们该怎么跳转呢?
总不能每次都将同一个函数放在下一条命令吧? 那样也太蠢了.</p>
<p>实际上答案就在上面的那段话里面了.</p>
<p>提示: 跳转.</p></li>
<li><p>那么知道这些有什么用呢?</p>
<p>我们可以根据这样简单的东西来写一个非常简单的小程序,
来模拟单核处理器是如何实现多进程的. 见小练习:</p></li>
</ul></li>
<li><p>小练习 - 虚假的多进程</p>
<ol>
<li><p>假设我们已经有了一个神奇的函数 <code>compile</code>,
能够将我们输入的代码变成一个神奇的指针 <code>p</code>,
指向我们代码的单步指令:</p>
<div class="sourceCode" id="cb25" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>command <span class="op">*</span>compile<span class="op">(</span><span class="dt">char</span> <span class="op">*</span>code<span class="op">)</span> <span class="op">{</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">// break the code into simple step code</span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> pointer_to_simple_code<span class="op">;</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>以及一个简单的指令 <code>eval</code>, 能够执行 <code>p</code>
指向位置的简单命令:</p>
<div class="sourceCode" id="cb26" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> eval<span class="op">(</span>command <span class="op">*</span>p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>  execute_command_at<span class="op">(</span>p<span class="op">);</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>如果你有兴趣的话, 可以先实现 <code>eval</code> 函数, 假设我们的
<code>simple_code</code> 的格式就是 asm code:</p>
<pre class="text"><code>&lt;asm_code&gt; ::= &lt;cmd&gt; &lt;args&gt;
</code></pre></li>
<li><p>我们打算有一个栈结构: (通过链表来实现)</p>
<div class="sourceCode" id="cb28" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> stack <span class="op">{</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> val<span class="op">;</span></span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> stack <span class="op">*</span>previous<span class="op">;</span></span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>请在阅读 <a
href="https://www.geeksforgeeks.org/what-is-linked-list/">链接</a>
后思考该如何实现一个 <code>push</code> 和一个 <code>pop</code>
的操作:</p>
<div class="sourceCode" id="cb29" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> push<span class="op">(</span><span class="dt">int</span> val<span class="op">,</span> <span class="kw">struct</span> stack <span class="op">*</span>s<span class="op">)</span> <span class="op">{</span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">struct</span> stack <span class="op">*</span>next <span class="op">=</span> <span class="op">(</span><span class="kw">struct</span> stack <span class="op">*)</span>malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span><span class="kw">struct</span> stack<span class="op">));</span></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a>  next<span class="op">-&gt;</span>val <span class="op">=</span> val<span class="op">;</span></span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a>  next<span class="op">-&gt;</span>previous <span class="op">=</span> s<span class="op">;</span></span>
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a>  s <span class="op">=</span> next<span class="op">;</span></span>
<span id="cb29-8"><a href="#cb29-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-9"><a href="#cb29-9" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> val<span class="op">;</span></span>
<span id="cb29-10"><a href="#cb29-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb29-11"><a href="#cb29-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-12"><a href="#cb29-12" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> pop<span class="op">(</span><span class="kw">struct</span> stack <span class="op">*</span>s<span class="op">)</span> <span class="op">{</span></span>
<span id="cb29-13"><a href="#cb29-13" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> val <span class="op">=</span> s<span class="op">-&gt;</span>val<span class="op">;</span></span>
<span id="cb29-14"><a href="#cb29-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-15"><a href="#cb29-15" aria-hidden="true" tabindex="-1"></a>  s <span class="op">=</span> s<span class="op">-&gt;</span>previous<span class="op">;</span></span>
<span id="cb29-16"><a href="#cb29-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb29-17"><a href="#cb29-17" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> val<span class="op">;</span></span>
<span id="cb29-18"><a href="#cb29-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>(注: 代码不一定正确, 并且请了解如何使用 <code>typeof</code>
来让上面的代码变得更美观. )</p>
<p>假设我们已经拥有了一个完美的栈 <code>stack</code>,
而不是上面的代码.</p></li>
<li><p>我们现在维护一个数组, 作为进程池,
其中每个元素都是一个指向我们子进程的一个运行指针.</p>
<p>那么思考该如何让他们同时, 或者说, 看起来同时进行? 提示:
周游历遍.</p></li>
<li><p>请在上面的提示的基础上, 给一个最粗略的多线程的例子.
并且了解程序的 interrupt. 给出上面的多线程的例子有什么不足之处,
以及可以如何改进. (optional)</p></li>
</ol></li>
</ol></li>
<li><p>程序的基本结构</p>
<p>我们以一个简单的程序为例:</p>
<pre class="text"><code>&lt;c_code&gt; ::= &lt;preprocess&gt;*
       &lt;function_declaration&gt;*
       &lt;main_function&gt;
       &lt;function_definition&gt;*
</code></pre></li>
<li><p>Preprocess</p>
<p>也就是预处理, 在这一部分, 我们会遇到引用头文件, 宏定义,
预编译等等的知识.</p>
<p>在 C 语言里, 宏虽然是叫 marco, 实际上和 Lisp 的 marco 系统比起来,
更像是一种字符串替换的系统.</p>
<ul>
<li><p><code>define</code></p>
<div class="sourceCode" id="cb31" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#define TRUE 1</span></span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#define FALSE 0</span></span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a><span class="pp">#define MAX(a, b) (a &gt; b ? a : b)</span></span></code></pre></div>
<p>实际上就是在编译的时候, 提前替换程序中所有和定义的字符串,
或者定义的形式相同的文本, 然后假装啥也没发生. 比如下面的代码,</p>
<div class="sourceCode" id="cb32" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">(</span>MAX<span class="op">(</span>x<span class="op">,</span> y<span class="op">)</span> <span class="op">&gt;</span> <span class="dv">3</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> TRUE<span class="op">;</span></span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> FALSE<span class="op">;</span></span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>经过替换后会变成:</p>
<div class="sourceCode" id="cb33" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="op">((</span>x <span class="op">&gt;</span> y <span class="op">?</span> x <span class="op">:</span> y<span class="op">)</span> <span class="op">&gt;</span> <span class="dv">3</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>之所以会这样做, 是为了减少我们在代码中使用不必要的魔法数字,
比如后者的 <code>1</code> 和 <code>0</code> 就会让人不知道他们的含义.
甚至如果你定义 <code>TRUE</code> 为 <code>666</code>, <code>FALSE</code>
为 <code>999</code>, 那么如果只看数字值的话,
你的代码就是一个很好的混淆的代码了.</p></li>
<li><p><code>if</code> 判断</p>
<p>在预编译的时候, 你可能还会接触到宏条件判断, 比如如果没有定义宏
<code>HELLO</code>, 那么定义宏 <code>HELLO</code>,
就可以用来防止重复定义:</p>
<div class="sourceCode" id="cb34" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#ifndef HELLO</span></span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#define HELLO</span></span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#endif</span></span></code></pre></div>
<p>类似的就不多展开了.</p></li>
<li><p><code>include</code></p>
<p>我们可能还想要在代码中引用其他已经写好了的库, 于是通过
<code>#include &lt;lib_name&gt;</code> 即可引用相应的库.
比如标准输入和输出的库:</p>
<div class="sourceCode" id="cb35" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdio.h&gt;</span></span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a>  printf<span class="op">(</span><span class="st">&quot;Hello</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div></li>
</ul></li>
<li><p>Main Function and Function Definition</p>
<p>一般而言, 一个 C 程序以 <code>main</code> 函数作为程序入口.
正如我们之前所介绍的子程序的例子一样.
我们可以将一个系统看作是一个大的主程序, 那么想要运行一个程序的时候,
就会将我们的程序先载入到内存中, 就像是古早的游戏机插上了游戏卡带
(实际并不是) 一样.</p>
<p>然后系统就会像是对待一个子程序一样对待我们的程序:
为其开辟栈来存放变量, 为其提供函数调用, 等等.</p>
<p>(Note: 因为编译器是线性读取代码的, 所以在处理函数跳转的位置的时候,
如果你的代码定义是在调用后的话, 编译器会不知道你在说什么, 或者说,
它不知道自己要跳转到哪里. 所以你会经常看见 C 代码里面会有
将定义写在前面的, 或者只写一句话定义的形式. )</p>
<div class="sourceCode" id="cb36" data-org-language="C"><pre
class="sourceCode c"><code class="sourceCode c"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdio.h&gt;</span></span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> foo<span class="op">();</span></span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb36-6"><a href="#cb36-6" aria-hidden="true" tabindex="-1"></a>  foo<span class="op">();</span></span>
<span id="cb36-7"><a href="#cb36-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb36-8"><a href="#cb36-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-9"><a href="#cb36-9" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> foo<span class="op">()</span> <span class="op">{</span></span>
<span id="cb36-10"><a href="#cb36-10" aria-hidden="true" tabindex="-1"></a>  puts<span class="op">(</span><span class="st">&quot;foo&quot;</span><span class="op">);</span></span>
<span id="cb36-11"><a href="#cb36-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div></li>
<li><p>Final</p>
<p>其实还有很多需要学, 不过目前只是提供了一个简单的形式框架.
可以参考:</p>
<ul>
<li><a href="https://www.geeksforgeeks.org">GeeksforGeeks</a></li>
<li><a
href="https://www.tutorialspoint.com/index.htm">TutorialsPoint</a></li>
<li><a
href="https://www.runoob.com/cprogramming/c-tutorial.html">菜鸟教程</a></li>
<li>等等</li>
</ul>
</details>
</li>
</ol>
<h3 id="项目一-点亮灯">项目一: 点亮灯</h3>
<p><strong>使用的平台</strong>: <a
href="https://wokwi.com">WOKWI</a></p>
<p><strong>学习内容</strong>:</p>
<ul>
<li>简单的 C 语法</li>
<li>了解函数体的形式, 以及会使用变量</li>
<li>了解基本的宏定义</li>
</ul>
<p>工程文件见 <code
class="verbatim">classes/winter-class-01/Blink</code> 文件夹.
将工程文件以次复制到平台上即可.</p>
<p><strong>观察</strong>:</p>
<ol>
<li>请先阅读代码并猜测其作用
<ul>
<li>函数中: <code class="verbatim">setup</code>, <code
class="verbatim">loop</code>, <code class="verbatim">pinMode</code>,
<code class="verbatim">digitalWrite</code>, <code
class="verbatim">delay</code> 的作用</li>
<li>其中的值 <code class="verbatim">LED</code>, <code
class="verbatim">9</code>, <code class="verbatim">1000</code>
对应什么</li>
</ul></li>
<li>请修改对应的值, 观测变化并验证你的猜测.</li>
</ol>
<p><strong>例</strong>:</p>
<ol>
<li><p>添加一个 <code class="verbatim">count</code> 变量,
定义其为全局变量.</p>
<ul>
<li>已知定义的语句如下: <code class="verbatim">int count;</code>,</li>
<li>已知其初始化的值应为 <code class="verbatim">count = 0</code>,</li>
<li>已知每次点亮灯都要对其进行增加操作 <code
class="verbatim">count++</code>.</li>
</ul>
<p>请修改代码满足要求.</p>
<p>(拓展: 这样的代码会有什么问题? 或者说是否有问题? )</p></li>
<li><p>添加一个 <code class="verbatim">if</code> 判断语句, 使得 <code
class="verbatim">count</code> 为 <span
class="math inline">ℤ/<em>m</em></span> 中元素. 其中 <span
class="math inline"><em>m</em></span> 为宏定义.</p>
<ul>
<li>已知 <code class="verbatim">if</code> 语句如下: <code
class="verbatim">if (&lt;condition&gt;) { &lt;code&gt; }</code>.</li>
<li>已知宏定义语句如下: <code
class="verbatim">#define M &lt;val&gt;</code>.</li>
</ul>
<p>请修改代码满足要求</p></li>
<li><p>在原有的代码基础上, 改变闪烁周期. 在一个闪烁序列中循环. 比如
<span class="math inline">$\bar{0}$</span> 时, <code
class="verbatim">delay(1000)</code>; 在 <span
class="math inline">$\bar{1}$</span> 时, <code
class="verbatim">delay(1100)</code> 等等.</p></li>
</ol>
<p><strong>作业</strong>: (代码见 <code
class="verbatim">classes/winter-class-01/Simple Digital Number</code>
文件夹)</p>
<ul>
<li>(课堂演示部分) 打开平台, 新建一个项目, 添加一个 <a
href="https://docs.wokwi.com/parts/wokwi-7segment">数字显示管</a>.</li>
<li>(课后编写) 让数字显示管能够显示不同的数字, 并间隔 1 秒轮换播放.</li>
<li>思考:
<ul>
<li>如何让代码变短?</li>
<li>如何控制更多的灯?</li>
</ul></li>
</ul>
<h2 id="第二节课">第二节课</h2>
<ul>
<li>学会查找库以及直接抄代码</li>
<li>学会写一些简单的程序</li>
</ul>
<h3 id="项目二-调用库以及通讯">项目二: 调用库以及通讯</h3>
<p><strong>使用的平台</strong>: <a
href="https://wokwi.com">WOKWI</a></p>
<p><strong>学习的内容</strong>:</p>
<ul>
<li>学会去找库并调用库</li>
<li>学会看库的说明文档</li>
<li>学会简单的串口通信</li>
</ul>
<p>工程文件见 <a
href="https://wokwi.com/projects/294342288335700490">官方示例文件</a>.</p>
<p><strong>观察</strong>:</p>
<ul>
<li><p>代码中的 <code
class="verbatim">#include &lt;LiquidCrystal.h&gt;</code> 做了什么. 这个
<a
href="https://www.arduino.cc/reference/en/libraries/liquidcrystal/">库</a>
干了什么, 有什么可以做的.</p></li>
<li><p>该怎么找到这样的库. 如果我们去买零件的话.</p>
<p>(注: 实际上一般来说, 如果是正经零件的话,
一般是不需要担心很多的东西的, 因为购买的时候会提供文档.
如果你是直接捡到了一个零件, 那么可以先找到其对应的代码, 比如 LCD 1602
即这个零件的型号代码. 然后在网络上搜索: "LCD 1602 Libraries", 或者 "LCD
1602 Datasheet" 等等. 并且因为我们的东西并不是很硬核, 实际上大多数时候,
你只需要搜索 "LCD 1602 Examples" 之类的大部分时候就可以了. )</p></li>
<li><p>尝试修改代码, 改变输出的内容. 在文档中找一个函数并试试看效果.
(比如 <code class="verbatim">createChar</code> 函数)</p></li>
</ul>
<p><strong>例</strong>:</p>
<ul>
<li>现在我们想要实现这样的功能, 能够向 Arduino 输入信息,
并让其显示在屏幕 (LCD 1602) 上.
<ul>
<li>已知 Arduino 等单片机通过 <a
href="https://www.oreilly.com/library/view/arduino-cookbook/9781449399368/ch04.html">串口</a>
和其他计算机进行通信. (<a
href="https://www.arduino.cc/reference/en/language/functions/communication/serial/">官方文档</a>,
或者 <a
href="https://docs.arduino.cc/learn/electronics/lcd-displays">LCD
1602</a>) 其中, 你可能需要了解:
<ul>
<li><p><a href="https://en.wikipedia.org/wiki/Baud">波特率</a>,
<del>你可以理解为打游戏的帧数, 不过这个帧数需要稳定.</del>
一般常见的波特率有 300, 1200, 2400, 9600, 19200, 38400, 115200
等.</p></li>
<li><p><a href="https://linuxhint.com/tx-rx-on-arduino/">TX, RX</a>,
<del>你可以理解为两根水管, 一根进一根出.</del> 通过这两条,
得以和其他设备进行沟通. (现实中的下载程序也就是使用这样的两条)</p></li>
<li><p>在 Arduino 中, 你可以通过这样来初始化一个串口:</p>
<pre class="arduino"><code>#define BAUD 9600;

void setup() {
  Serial.begin(BAUD);
}
</code></pre></li>
<li><p>通过 <code class="verbatim">Serial.println</code> 和 <code
class="verbatim">Serial.read</code> 来做到简单的输出和输入.</p>
<p>(注: 这样的方式好么? )</p></li>
</ul></li>
</ul></li>
</ul>
<p><strong>作业</strong>:</p>
<ul>
<li>(选做) 写一个简单的四则运算的计算器</li>
</ul>
<h2 id="第三节课">第三节课</h2>
<ul>
<li>本节课类似于阶段性考核, 需要上传代码 (或者运行的截图或者视频或者 gif
都行)</li>
<li>本节课不会涉及过多的新知识, 所以可以放心跷课</li>
</ul>
<h3 id="项目三-给上节课的计算器加一个漂亮的按键板">项目三:
给上节课的计算器加一个漂亮的按键板</h3>
<ul>
<li>该项目为本节课的主要内容</li>
</ul>
<p><strong>使用的平台</strong>: <a
href="https://wokwi.com">WOKWI</a></p>
<p>要求:</p>
<ul>
<li>加一个 Keypad 来通过按键来进行输入</li>
<li>实现浮点数运算</li>
<li>美化输出形式</li>
<li>美化代码</li>
<li>(可选) 实现分数计算而不是浮点数运算</li>
</ul>
<p>了解的内容:</p>
<ul>
<li>代码重构</li>
<li>项目的组织方法
<ul>
<li>MVC
<ul>
<li><p>Model - 数据</p>
<p>如何组织存储我们的数据</p></li>
<li><p>View - 视图, 显示, 交互</p>
<p>UI 界面</p></li>
<li><p>Control - 控制</p>
<p>一些控制代码</p></li>
</ul></li>
</ul></li>
</ul>
<h3 id="项目四-做一个数据收集器">项目四: 做一个数据收集器</h3>
<ul>
<li>该项目的数据主要为瞎掰, 如果有真实的单片机的话可以尝试自行搭建</li>
<li>该项目即为大部分硬件组的主要的核心</li>
</ul>
<p><strong>使用的平台</strong>: <a href="https://wokwi.com">WOKWI</a>,
实体环境 (更加推荐)</p>
<p>要求:</p>
<ul>
<li>能够接受各种传感器的数据输入</li>
<li>通过 <code class="verbatim">Serial</code> 将数据格式化输出</li>
<li>(可选) 在本地用 Python 或者其他程序读取信息并储存到 CSV 中</li>
</ul>
<h3 id="项目五-做一个服务器">项目五: 做一个服务器</h3>
<ul>
<li>该项目主要通过分解去年的项目代码为主</li>
<li>如果有兴趣的话可以重新写</li>
</ul>
<p><strong>使用的平台</strong>: <a href="https://wokwi.com">WOKWI</a>,
实体环境 (更加推荐)</p>
<p>要求:</p>
<ul>
<li><p>能够显示一个简单的小网页 (不用美观也可以)</p>
<p><del>可以考虑去前端组蹭蹭课</del></p></li>
<li><p>然后通过一个网页控件来控制</p></li>
</ul>
<h3 id="项目六-可选">项目六: (可选)</h3>
<ul>
<li>如果有时间的话, 随便写点代码吧</li>
</ul>
<h1 id="文献调研">文献调研</h1>
<p>阅读 iGEM Best Hardware 的项目, 了解:</p>
<ul>
<li>干了什么, 主要用了什么样的技术和方法 (?)</li>
<li>有什么特别的亮点</li>
</ul>
</body>
</html>
